package cn.lyjuan.payment.third.base.pay.chl.impl.iap.util;

import cn.lyjuan.base.util.HttpUtils;
import cn.lyjuan.payment.third.base.pay.chl.impl.iap.bean.IapPayReq;
import cn.lyjuan.payment.third.base.pay.chl.impl.iap.bean.IosVerifyRes;
import cn.lyjuan.payment.third.base.pay.chl.impl.iap.bean.LatestReceiptInfoVo;
import cn.lyjuan.payment.third.base.pay.chl.impl.iap.bean.ReceiptVo;
import cn.lyjuan.payment.third.base.pay.chl.impl.iap.enums.IosDevelopEnum;
import cn.lyjuan.payment.third.base.pay.vo.res.BaseRes;
import com.google.gson.Gson;
import com.google.gson.internal.LinkedTreeMap;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.util.StringUtils;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * @author arvin
 * desc payment-third cn.lyjuan.payment.third.base.pay.chl.impl.iap.util
 * 2021/3/8 17:09
 */
@Slf4j
@SuppressWarnings("all")
public class IapSubmit {

    private static Gson gson;

    private static final int SO_TIMEOUT = 5000;

    private static final int CONNECTION_TIMEOUT = 5000;

    private static final String CHARSET = "UTF-8";

    public static Map<String, String> headers = null;

    public static final SimpleDateFormat sdf = new SimpleDateFormat("");

    private static final String url_sandbox = "https://sandbox.itunes.apple.com/verifyReceipt";
    private static final String url_verify = "https://buy.itunes.apple.com/verifyReceipt";

    static {
        gson = new Gson();
    }

    /**
     * 检查参数
     *
     * @param params 参数
     * @return BaseRes
     */
    public static BaseRes checkPayReq(IapPayReq params) {
        return new BaseRes(true);
//        if (params == null ||
//                StringUtils.isNull(params.getAppNo()) ||
//                StringUtils.isNull(params.getReceipt()) ||
//                StringUtils.isNull(params.getIosGoodsId()) ||
//                StringUtils.isNull(params.getTransactionId())
//        ) {
//            return new BaseRes(false, "param is not valid: [appNo,receipt,iosGoodsId] one of them is empty");
//        }
//        return new BaseRes(true);
    }

    /**
     * 验证内购
     *
     * @param receipt       receipt
     * @param password      password
     * @param productId     productId
     * @param transactionId transactionId
     * @param isDev         isDev
     * @return LatestReceiptInfoVo
     */
    public static LatestReceiptInfoVo verify(String receipt, String password, String productId, String transactionId, boolean isDev) {
        // exec
        String verifyResult = IapSubmit.getAppVerifyResponse(receipt, password, isDev);
        if (verifyResult.equals("")) {
            log.error("doIosRequest but none response: transactionID: [{}]", transactionId);
            return null;
        }
        // 反序列化
        IosVerifyRes res = parseIapRes(verifyResult);

        if (res == null) {
            log.error("iap verify parse result error ==> receipt: [{}] password: [{}] productId: [{}] transactionId: [{}] isDev: [{}]",
                    receipt, password, productId, transactionId, isDev);
            return null;
        }
        Integer status = res.getStatus();
        String env = res.getEnvironment();
        // 沙盒环境，应沙盒测试，否则执行下面
        if (!isDev && (21007 == status || IosDevelopEnum.Sandbox.getValue().equalsIgnoreCase(env))) {
            //2.再沙盒测试  发送平台验证
            verifyResult = IapSubmit.getAppVerifyResponse(receipt, password, true);
            log.debug("iap sandbox retry result ==> [{}]", verifyResult);
            res = parseIapRes(verifyResult);
            status = res.getStatus();
        }
        // verify success
        if (status == 0) {
            ReceiptVo receiptVo = res.getReceipt();
            List<LatestReceiptInfoVo> listInfoVo = receiptVo.getIn_app();
            if (listInfoVo == null || listInfoVo.isEmpty()) {
                log.error("iap verify with null receiptVo Error: Jailbreak : transactionId: [{}]", transactionId);
                return null;
            }
            // find data
            // check data
            return getLatestData(listInfoVo, productId);
        }
        // verify fail
        return null;
    }

    /**
     * 解析 IosVerifyRes
     *
     * @param verifyResult verifyResult
     * @return IosVerifyRes
     */
    public static IosVerifyRes parseIapRes(String verifyResult) {
        if (!StringUtils.hasLength(verifyResult)) {
            return null;
        }
        @SuppressWarnings("unchecked")
        Map<String, Object> map = gson.fromJson(verifyResult, Map.class);
        IosVerifyRes res = new IosVerifyRes();
        if (map.get("status") != null) {
            res.setStatus((int) Double.parseDouble(String.valueOf(map.get("status"))));
        }
        if (StringUtils.hasLength((String) map.get("environment"))) {
            String env = String.valueOf(map.get("environment"));
            res.setEnvironment(env);
        }
        if (map.get("receipt") != null) {
            try {
                LinkedTreeMap<String, Object> linkedTreeMap = (LinkedTreeMap<String, Object>) map.get("receipt");
                List<LinkedTreeMap<String, Object>> list2 = (List<LinkedTreeMap<String, Object>>) linkedTreeMap.get("in_app");
                //
                List<LatestReceiptInfoVo> list = new ArrayList<>();
                for (LinkedTreeMap<String, Object> tmpItem : list2) {
                    LatestReceiptInfoVo tmpVo = parseResToLatestReceiptInfoVo(tmpItem);
                    list.add(tmpVo);
                }
                //
                ReceiptVo receiptVo1 = new ReceiptVo();
                receiptVo1.setIn_app(list);
                res.setReceipt(receiptVo1);
            } catch (Exception e) {
                log.error("IapSubmit parseIapRes from: [{}] error: [{}]", verifyResult, e.getMessage());
                return null;
            }
        }
        return res;
    }

    /**
     * 转换数据到 LatestReceiptInfoVo
     *
     * @param data LinkedTreeMap
     * @return LatestReceiptInfoVo
     */
    private static LatestReceiptInfoVo parseResToLatestReceiptInfoVo(LinkedTreeMap<String, Object> data) {
        LatestReceiptInfoVo res = new LatestReceiptInfoVo();
        res.setCancellation_date(convertNullAbleObjectToString(data.get("cancellation_date")));
        res.setCancellation_date_ms(convertNullAbleObjectToString(data.get("cancellation_date_ms")));
        res.setOriginal_transaction_id(convertNullAbleObjectToString(data.get("original_transaction_id")));
        res.setProduct_id(convertNullAbleObjectToString(data.get("product_id")));
        res.setPurchase_date(convertNullAbleObjectToString(data.get("purchase_date")));
        res.setPurchase_date_ms(convertNullAbleObjectToString(data.get("purchase_date_ms")));
        res.setTransaction_id(convertNullAbleObjectToString(data.get("transaction_id")));
        return res;
    }

    /**
     * parseResToLatestReceiptInfoVo 专用
     *
     * @param obj obj
     * @return String
     */
    private static String convertNullAbleObjectToString(Object obj) {
        if (obj == null) {
            return null;
        }
        return obj.toString();
    }

    /**
     * 校验 appNo 和 productId(空,则不验证) 列表中匹配到的最新一条数据
     *
     * @param listInfoVo listInfoVo
     * @param productId  productId
     * @return LatestReceiptInfoVo
     */
    public static LatestReceiptInfoVo getLatestData(List<LatestReceiptInfoVo> listInfoVo, String productId) {
        int num = 0;
        String tmpDate = "";
        boolean tag = productId == null || productId.length() == 0;
        for (int i = 0; i < listInfoVo.size(); i++) {
            if (listInfoVo.get(i) != null) {
                LatestReceiptInfoVo tmpData = listInfoVo.get(i);
                // check ios goodsId
                if (tag && !listInfoVo.get(i).getProduct_id().equals(productId)) {
                    continue;
                }
                // sort by date_ms
                String tmpDataDate;
                if (tmpData.getPurchase_date() != null) {
                    tmpDataDate = tmpData.getPurchase_date_ms();
                } else {
                    tmpDataDate = tmpData.getCancellation_date_ms();
                }
                if (tmpDate.compareTo(tmpDataDate) > 0) {
                    tmpDate = tmpDataDate;
                    num = i;
                }
            }
        }
        return listInfoVo.get(num);
    }

    /**
     * 校验 列表中匹配到的最原始的数据
     *
     * @param listInfoVo listInfoVo
     * @param iosGoodsId iosGoodsId
     * @return LatestReceiptInfoVo
     */
    public static LatestReceiptInfoVo getOriginData(List<LatestReceiptInfoVo> listInfoVo, String iosGoodsId) {
        int num = 0;
        String tmpDate = null;
        boolean tag = iosGoodsId == null || iosGoodsId.length() == 0;
        for (int i = 0; i < listInfoVo.size(); i++) {
            if (listInfoVo.get(i) != null) {
                LatestReceiptInfoVo tmpData = listInfoVo.get(i);
                if (tmpDate == null) {
                    tmpDate = tmpData.getPurchase_date_ms();
                }
                // check ios goodsId
                if (tag && !tmpData.getProduct_id().equals(iosGoodsId)) {
                    continue;
                }
                // sort by date_ms
                String tmpDataDate;
                if (tmpData.getPurchase_date() != null) {
                    tmpDataDate = tmpData.getPurchase_date_ms();
                } else {
                    tmpDataDate = tmpData.getCancellation_date_ms();
                }
                if (tmpDate.compareTo(tmpDataDate) < 0) {
                    tmpDate = tmpDataDate;
                    num = i;
                }
            }
        }
        return listInfoVo.get(num);
    }

    /**
     * 排序 从小到大
     *
     * @param list list
     */
    public static void sort(List<LatestReceiptInfoVo> list) {
        list.sort(Comparator.comparing(LatestReceiptInfoVo::getPurchase_date));
    }

    /**
     * 苹果服务器内购验证
     *
     * @param receipt 账单
     * @param isDev   是否沙箱模式
     * @return null 或返回结果 沙盒 https://sandbox.itunes.apple.com/verifyReceipt
     */
    public static String getAppVerifyResponse(String receipt, String password, boolean isDev) {
        String url;
        //沙盒测试
        if (isDev) {
            url = url_sandbox;
        }
        //线上测试
        else {
            url = url_verify;
        }
        try {
            String json = "{\"receipt-data\":\"" + receipt + "\",\"password\":\"" + password + "\"}";
            String result = HttpUtils.sendPost(url, json, false, StandardCharsets.UTF_8.name(), null);
            if (log.isDebugEnabled())
                log.debug("iap verify ==> url: {}, result: {}", url, result);
            return result;
        } catch (Exception ex) {
            log.error("apple store server error: [{}]", ex.getMessage());
            throw new RuntimeException(ex);
        }
    }

    /**
     * httpPost - 特供iap
     *
     * @param url  url
     * @param json map
     * @return String
     */
    public static String doPost(String url, String json) {
        // set req and conn timeout
        RequestConfig requestConfig = RequestConfig
                .custom()
                .setSocketTimeout(SO_TIMEOUT)
                .setConnectTimeout(CONNECTION_TIMEOUT)
                .build();
        if (headers == null) {
            headers = new HashMap<>();
            headers.put("Content-Type", "application/json");
            headers.put("Connection", "Keep-Alive");
        }
        return postBody(url, requestConfig, headers, json);
    }

    /**
     * 基于HttpClient 4.3的通用POST方法
     *
     * @param url  提交的URL
     * @param json 提交<参数，值>Map
     * @return 提交响应
     */
    private static String postBody(String url, RequestConfig requestConfig, Map<String, String> headers, String json) {
        CloseableHttpClient client = HttpClients.createDefault();
        String responseText = "";
        CloseableHttpResponse response = null;
        try {
            HttpPost httpPost = new HttpPost(url);
            // set config
            httpPost.setConfig(requestConfig);
            // set headers
            if (headers != null) {
                for (Map.Entry<String, String> tmpMap : headers.entrySet()) {
                    if (tmpMap != null) {
                        httpPost.addHeader(tmpMap.getKey(), tmpMap.getValue());
                    }
                }
            }
            // set params
//            if (paramsMap != null) {
//                List<NameValuePair> paramList = new ArrayList<>();
//                for (Map.Entry<String, String> param : paramsMap.entrySet()) {
//                    NameValuePair pair = new BasicNameValuePair(param.getKey(), param.getValue());
//                    paramList.add(pair);
//                }
//                httpPost.setEntity(new UrlEncodedFormEntity(paramList, CHARSET));
//            }
            StringEntity stringEntity = new StringEntity(json);
            httpPost.setEntity(stringEntity);
            // exec
            response = client.execute(httpPost);
            log.debug("exec iap post response: [{}]", response);
            // parse entity
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                responseText = EntityUtils.toString(entity, CHARSET);
            }
        } catch (Exception e) {
            log.error("httpPost body error: [{}]  and  url: [{}] and params: [{}] and header: [{}]", e.getMessage(), url, json, headers);
        } finally {
            try {
                response.close();
            } catch (Exception e) {
                log.error("http post error: close http connection error: [{}]", e.getMessage());
            }
        }
        return responseText;
    }

    /**
     * 用BASE64加密
     *
     * @param str str
     * @return String
     */
    public static String getBASE64(String str) {
        byte[] b = str.getBytes();
        return new BASE64Encoder().encode(b);
    }

    public static String convertBASE64(String base64Str) {
        try {
            byte[] b = new BASE64Decoder().decodeBuffer(base64Str);
            return new String(b);
        } catch (IOException e) {
            log.error("IapSubmit convertBase64 str: [{}] error: [{}]", base64Str, e);
        }
        return null;
    }

    /**
     * 转换 ISO8601到LocalDateTime
     *
     * @param date date
     * @return LocalDateTime
     */
    public static LocalDateTime convertStringToISO8601Date(String date) {
        return LocalDateTime.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss 'Etc/GMT'"));
    }
}
